package AgendaColaborativa;

import beans.Evento;
import beans.Usuario;
import exceptions.AmigoJaAdicionadoException;
import exceptions.AtributoNaoPreenchidoException;
import exceptions.EventoInexistenteException;
import exceptions.LoginInvalidoException;
import exceptions.SenhaInvalidaException;
import exceptions.UsuarioJaExisteException;
import exceptions.UsuarioNaoAmigoException;
import exceptions.UsuarioNaoCadastradoException;
import exceptions.UsuarioNaoDonoException;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import persistencia.PersistenciaUsuario;

/**
 *
 * @author Everton de Oliveira & Robson Medeiros
 * Sistema agenda colaborativa
 */
public class AgendaColaborativa {

    private Map<String, Usuario> usuarios = new HashMap<String, Usuario>();
    private Map<Integer, Evento> eventos = new HashMap<Integer, Evento>();
    private Logger logger = Logger.getLogger(AgendaColaborativa.class);
    SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
    private PersistenciaUsuario persistencia = new PersistenciaUsuario();

    public AgendaColaborativa(){
        PropertyConfigurator.configure("log4j/log4j.properties");
    }

    /**
     *
     * @param login - string para identificar o usuário
     * @return usuario - dados do usuário
     * @throws LoginInvalidoException - login nao pode ser validado
     * @throws UsuarioNaoCadastradoException - usuário não cadastrado
     */
    public String getNomeUsuario(String login) throws LoginInvalidoException, UsuarioNaoCadastradoException {
        if (login == null || login.equals("")) {
            logger.info("Método getNomeUsuario. Houve Exception de Login inválido, Login do usuário: " + login);
            throw new LoginInvalidoException();
        } else {
            if (!this.contemUsuario(login)) {
                logger.info("Método getNomeUsuario. Houve Exception de Usuário não cadastrado, Login do usuário: " + login);
                throw new UsuarioNaoCadastradoException();
            } else {
                String usuario = this.usuarios.get(login).getNome();
                logger.info("Método getNomeUsuario. Houve Sucesso, Login do usuário: " + login + " e Retorno: " + usuario);
                return usuario;
            }
        }
    }

    /**
     *
     * @param login - String
     * @param senha - String
     * @param nome - String
     * @throws SenhaInvalidaException - senha não aceita
     * @throws UsuarioJaExisteException - usuário não cadastrado
     * @throws LoginInvalidoException - login não pode ser validado
     * cria um usuário para a agenda
     */
    public void criarUsuario(String login, String senha, String nome) throws SenhaInvalidaException, UsuarioJaExisteException, LoginInvalidoException {
        if (login == null || login.equals("")) {
            logger.info("Método criarUsuario. Houve Exception de Login inválido, Login do usuário: " + login);
            throw new LoginInvalidoException();
        }
        if (senha == null || senha.equals("") || (senha.length() <= 5)) {
            logger.info("Método criarUsuario. Houve Exception de Senha inválida, Senha do usuário: " + senha);
            throw new SenhaInvalidaException();
        }
        if (this.contemUsuario(login)) {
            logger.info("Método criarUsuario. Houve Exception de Usuário ja existente, Login do usuário: " + login + " e Senha do usuário: " + senha);
            throw new UsuarioJaExisteException();
        }
        logger.info("Método criarUsuario. Houve Sucesso, Login do usuário: " + login + " e Senha do usuário: " + senha);
        Usuario usuario = new Usuario(login, senha, nome);
        this.usuarios.put(login, usuario);
    }

    /**
     *
     * @param login
     * @return boolean
     *  verificando se existe ou não usuario
     */
    public boolean contemUsuario(String login) {
        if (this.usuarios.containsKey(login)) {
            logger.info("Método contemUsuario. TRUE, Login do usuário: " + login);
            return true;
        }
        logger.info("Método contemUsuario. FALSE, Login do usuário: " + login);
        return false;
    }

    /**
     * limpa a lista de usuários
     */
    public void limpaSistema() {
        logger.info("Método limpaSistema. Limpando...");
        this.usuarios.clear();
    }

    /**
     *
     * @param login
     * @param senha
     * @return descrição do usuário
     * @throws AtributoNaoPreenchidoException - exceção de atribut não preechido
     * @throws UsuarioNaoCadastradoException - usuário não contém no sistema
     */
    public String getDescricao(String login, String senha) throws AtributoNaoPreenchidoException, UsuarioNaoCadastradoException {
        if (!contemUsuario(login)) {
            logger.info("Método getDescricao. Houve Exception de Usuário não cadastrado, Login do usuário: " + login);
            throw new UsuarioNaoCadastradoException();
        }
        if (this.usuarios.get(login).getDescricao() == null || this.usuarios.get(login).getDescricao().equals("")) {
            logger.info("Método getDescricao. Houve Exception de Atributo não preenchido, Login do usuário: " + login);
            throw new AtributoNaoPreenchidoException();
        }
        logger.info("Método getDescricao. Houve Sucesso, Login do usuário: " + login + " e Retorno: " + this.usuarios.get(login).getDescricao());
        return this.usuarios.get(login).getDescricao();
    }

    /**
     *
     * @param login
     * @param senha
     * @return estado civil do usuário
     * @throws AtributoNaoPreenchidoException
     * @throws UsuarioNaoCadastradoException
     */
    public String getEstadoCivil(String login, String senha) throws AtributoNaoPreenchidoException, UsuarioNaoCadastradoException {
        if (!contemUsuario(login)) {
            logger.info("Método getEstadoCivil. Houve Exception de Usuário não cadastrado, Login do usuário: " + login);
            throw new UsuarioNaoCadastradoException();
        }
        if (this.usuarios.get(login).getEstadoCivil() == null || this.usuarios.get(login).getEstadoCivil().equals("")) {
            logger.info("Método getEstadoCivil. Houve Exception de Atributo não preenchido, Login do usuário: " + login);
            throw new AtributoNaoPreenchidoException();
        }
        logger.info("Método getEstadoCivil. Houve Sucesso, Login do usuário: " + login + " e Retorno: " + this.usuarios.get(login).getEstadoCivil());
        return this.usuarios.get(login).getEstadoCivil();
    }

    /**
     *
     * @param login
     * @param senha
     * @return data de aniversário do usuário
     * @throws AtributoNaoPreenchidoException
     * @throws UsuarioNaoCadastradoException
     */
    public String getAniversario(String login, String senha) throws AtributoNaoPreenchidoException, UsuarioNaoCadastradoException {
        if (!contemUsuario(login)) {
            logger.info("Método getAniversario. Houve Exception de Usuário não cadastrado, Login do usuário: " + login);
            throw new UsuarioNaoCadastradoException();
        }
        if (this.usuarios.get(login).getAniversario() == null || this.usuarios.get(login).getAniversario().equals("")) {
            logger.info("Método getAniversario. Houve Exception de Atributo não preenchido, Login do usuário: " + login);
            throw new AtributoNaoPreenchidoException();
        }
        logger.info("Método getAniversario. Houve Sucesso, Login do usuário: " + login + " e Retorno: " + this.usuarios.get(login).getAniversario());
        return sdf.format( this.usuarios.get(login).getAniversario() );
    }

    /**
     *
     * @param login
     * @param senha
     * @return string - cidade natal do usuário
     * @throws AtributoNaoPreenchidoException - atributo não foi preenchido
     */
    public String getCidadeNatal(String login, String senha) throws AtributoNaoPreenchidoException, UsuarioNaoCadastradoException {
        if (!contemUsuario(login)) {
            logger.info("Método getCidadeNatal. Houve Exception de Usuário não cadastrado, Login do usuário: " + login);
            throw new UsuarioNaoCadastradoException();
        }
        if (this.usuarios.get(login).getCidadeNatal() == null || this.usuarios.get(login).getCidadeNatal().equals("")) {
            logger.info("Método getCidadeNatal. Houve Exception de Atributo não preenchido, Login do usuário: " + login);
            throw new AtributoNaoPreenchidoException();
        }
        logger.info("Método getCidadeNatal. Houve Sucesso, Login do usuário: " + login + " e Retorno: " + this.usuarios.get(login).getCidadeNatal());
        return this.usuarios.get(login).getCidadeNatal();
    }

    /**
     *
     * @param login
     * @param senha
     * @param novaDescricao - nova descriçao para ser atualizada
     * @throws SenhaInvalidaException - senha incorreta!
     */
    public void editarDescricao(String login, String senha, String novaDescricao) throws SenhaInvalidaException {
        if (!this.usuarios.get(login).getSenha().equals(senha)) {
            logger.info("Método editarDescricao. Houve Exception de Senha inválida, Senha do usuário: " + senha);
            throw new SenhaInvalidaException("Senha inválida.");
        }
        logger.info("Método editarDescricao. Houve Sucesso, Login do usuário: " + login + " Senha: " + senha + " e descrição: " + novaDescricao);
        this.usuarios.get(login).setDescricao(novaDescricao);
    }

    /**
     * 
     * @param login
     * @param senha
     * @param novoEstadoCivil 
     * @throws SenhaInvalidaException - senha incorreta!
     */
    public void editarEstadoCivil(String login, String senha, String novoEstadoCivil) throws SenhaInvalidaException {
        if (!this.usuarios.get(login).getSenha().equals(senha)) {
            logger.info("Método editarEstadoCivil. Houve Exception de Senha inválida, Senha do usuário: " + senha);
            throw new SenhaInvalidaException("Senha inválida.");
        }
        logger.info("Método editarEstadoCivil. Houve Sucesso, Login do usuário: " + login + " Senha: " + senha + " e estado civil: " + novoEstadoCivil);
        this.usuarios.get(login).setEstadoCivil(novoEstadoCivil);
    }

    /**
     *
     * @param login
     * @param senha
     * @param novoAniversario - "edita o aniversário
     * @throws SenhaInvalidaException
     */
    public void editarAniversario(String login, String senha, Date novoAniversario) throws SenhaInvalidaException {
        if (!this.usuarios.get(login).getSenha().equals(senha)) {
            logger.info("Método editarAniversario. Houve Exception de Senha inválida, Senha do usuário: " + senha);
            throw new SenhaInvalidaException("Senha inválida.");
        }
        logger.info("Método editarAniversario. Houve Sucesso, Login do usuário: " + login + " Senha: " + senha + " e aniversário: " + novoAniversario);
        this.usuarios.get(login).setAniversario(novoAniversario);
    }

    /**
     *
     * @param login
     * @param senha
     * @param novaCidade
     * @throws SenhaInvalidaException
     */
    public void editarCidadeNatal(String login, String senha, String novaCidade) throws SenhaInvalidaException {
        if (!this.usuarios.get(login).getSenha().equals(senha)) {
            logger.info("Método editarCidadeNatal. Houve Exception de Senha inválida, Senha do usuário: " + senha);
            throw new SenhaInvalidaException("Senha inválida.");
        }
        logger.info("Método editarCidadeNatal. Houve Sucesso, Login do usuário: " + login + " Senha: " + senha + " e cidade natal: " + novaCidade);
        this.usuarios.get(login).setCidadeNatal(novaCidade);
    }

    /**
     *
     * @param login
     * @param amigo
     * @return boolean - para verificar se é ou não é amigo
     */
    public boolean ehAmigo(String login, String amigo) {
        if (this.usuarios.get(login).getListaAmigos().contains(this.usuarios.get(amigo))) {
            logger.info("Método ehAmigo. TRUE, Amigos de logins: " + login + " e " + amigo);
            return true;
        }
        logger.info("Método ehAmigo. FALSE, Amigos de logins: " + login + " e " + amigo);
        return false;
    }

    /**
     *
     * @param id
     * @param amigo
     * @throws AmigoJaAdicionadoException - amigo ja esta adicionado anteriormente
     * @throws UsuarioNaoCadastradoException - usuário não existe no sistema
     */
    public void adicionarAmigo(String id, String amigo) throws AmigoJaAdicionadoException, UsuarioNaoCadastradoException {
        if (!this.contemUsuario(id) || !this.contemUsuario(amigo)) {
            logger.info("Método adicionarAmigo. Houve Exception de Usuário não cadastrado, Amigos de logins: " + id + " e " + amigo);
            throw new UsuarioNaoCadastradoException();
        }
        if (this.ehAmigo(id, amigo)) {
            logger.info("Método adicionarAmigo. Houve Exception de Amigo ja adicionado, Amigos de logins: " + id + " e " + amigo);
            throw new AmigoJaAdicionadoException();
        }
        if (this.usuarios.get(id).equals(this.usuarios.get(amigo))) {
            logger.info("Método adicionarAmigo. Houve Exception de Amigo ja adicionado, Amigos de logins: " + id + " e " + amigo);
            throw new AmigoJaAdicionadoException("Usuário não pode adicionar a si mesmo como amigo.");
        }
        if ((this.usuarios.get(id).getPendentes().contains(this.usuarios.get(amigo)))) {
            logger.info("Método adicionarAmigo. Houve Exception de Amigo ja adicionado, Amigos de logins: " + id + " e " + amigo);
            throw new AmigoJaAdicionadoException("Usuário já está adicionado como amigo, esperando aceitação do convite.");
        }
        this.usuarios.get(id).getPendentes().add(this.usuarios.get(amigo));
        if (this.usuarios.get(id).getPendentes().contains(this.usuarios.get(amigo)) && this.usuarios.get(amigo).getPendentes().contains(this.usuarios.get(id))) {
            logger.info("Método adicionarAmigo. Houve Sucesso, Amigos de logins: " + id + " e " + amigo);
            this.usuarios.get(id).getListaAmigos().add(this.usuarios.get(amigo));
            this.usuarios.get(amigo).getListaAmigos().add(this.usuarios.get(id));
            this.usuarios.get(id).getPendentes().remove(this.usuarios.get(amigo));
            this.usuarios.get(amigo).getPendentes().remove(this.usuarios.get(id));
        }
    }

    /**
     *
     * @param login
     * @return usuário - para a procurar um amigo
     */
    public String getAmigos(String login) {
        logger.info("Método getAmigos. Houve Sucesso, Login do usuário: " + login + " e Retorno: " + this.usuarios.get(login).getAmigos());
        return this.usuarios.get(login).getAmigos();
    }

    /**
     *
     * @param login
     * @return usuário
     * @throws LoginInvalidoException - login errado
     * @throws UsuarioNaoCadastradoException - usuário não existe no sistema
     */
    public Usuario getUsuario(String login) throws LoginInvalidoException, UsuarioNaoCadastradoException {
        if (login == null || login.equals("")) {
            logger.info("Método getUsuario. Houve Exception de Login inválido, Login do usuário: " + login);
            throw new LoginInvalidoException();
        }
        if (!this.contemUsuario(login)) {
            logger.info("Método getUsuario. Houve Exception de Usuário não cadastrado, Login do usuário: " + login);
            throw new UsuarioNaoCadastradoException();
        }
        logger.info("Método getUsuario. Houve Sucesso, Login do usuário: " + login);
        Usuario usuario = this.usuarios.get(login);
        return usuario;

    }

    /**
     * cria um evento com dono do evento, título,etc.
     * @param dono
     * @param titulo
     * @param data
     * @param local
     * @return string com o novo ID
     * @throws LoginInvalidoException
     * @throws UsuarioNaoCadastradoException
     */
    public Integer criarEvento(String dono, String titulo, String data, String local) throws LoginInvalidoException, UsuarioNaoCadastradoException, ParseException {
        if (this.eventos.isEmpty()) {
            Evento evento = new Evento(1, getUsuario(dono), titulo, sdf.parse(data), local);
            eventos.put(1, evento);
            this.eventos.get(1).getListaParticipantes().add(this.getUsuario(dono));
            this.usuarios.get(dono).getListaEventos().add(evento);
            this.usuarios.get(dono).getListaEventosParticipa().add(evento);
            logger.info("Método criarEvento. Houve Sucesso, Dono do evento: " + dono + " Título do evento: " + titulo + " Data do evento: " + data + " Local do evento: " + local);
            return 1;
        } else {
            Integer novoID = this.eventos.size() + 1;
            Evento evento = new Evento(novoID, getUsuario(dono), titulo, sdf.parse(data), local);
            eventos.put(novoID, evento);
            this.eventos.get(novoID).getListaParticipantes().add(this.getUsuario(dono));
            this.usuarios.get(dono).getListaEventos().add(evento);
            this.usuarios.get(dono).getListaEventosParticipa().add(evento);
            logger.info("Método criarEvento. Houve Sucesso, Dono do evento: " + dono + " Título do evento: " + titulo + " Data do evento: " + data + " Local do evento: " + local);
            return novoID;
        }
    }

    /**
     *
     * @param idEvento
     * @return - string - data do evento
     */
    public String getDataEvento(Integer idEvento) throws EventoInexistenteException {
        if (!this.eventos.containsKey(idEvento)){
            logger.info("Método getDataEvento. Houve Exception de Evento inexistente, id do evento: " + idEvento);
            throw new EventoInexistenteException();
        }
        logger.info("Método getDataEvento. Houve Sucesso, id do evento: " + idEvento + " e Retorno: " + this.eventos.get(idEvento).getData());
        return sdf.format(this.eventos.get(idEvento).getData());
    }

    /**
     *
     * @param idEvento
     * @return string - nome do evento
     */
    public String getTituloEvento(Integer idEvento) throws EventoInexistenteException {
        if (!this.eventos.containsKey(idEvento)){
            logger.info("Método getTituloEvento. Houve Exception de Evento inexistente, id do evento: " + idEvento);
            throw new EventoInexistenteException();
        }
        logger.info("Método getTituloEvento. Houve Sucesso, id do evento: " + idEvento + " e Retorno: " + this.eventos.get(idEvento).getTitulo());
        return this.eventos.get(idEvento).getTitulo();
    }

    /**
     *
     * @param idEvento
     * @return string - local onde evento será realizado
     */
    public String getLocalEvento(Integer idEvento) throws EventoInexistenteException {
        if (!this.eventos.containsKey(idEvento)){
            logger.info("Método getLocalEvento. Houve Exception de Evento inexistente, id do evento: " + idEvento);
            throw new EventoInexistenteException();
        }
        logger.info("Método getLocalEvento. Houve Sucesso, id do evento: " + idEvento + " e Retorno: " + this.eventos.get(idEvento).getLocal());
        return this.eventos.get(idEvento).getLocal();
    }

    /**
     *
     * @param idEvento
     * @return string -retornando o dono do evento
     */
    public String getDonoEvento(Integer idEvento) {
        logger.info("Método getDonoEvento. Houve Sucesso, id do evento: " + idEvento + " e Retorno: " + this.eventos.get(idEvento).getDono().getLogin());
        return this.eventos.get(idEvento).getDono().getLogin();
    }

    /**
     *
     * @param idEvento
     * @return map - evento
     */
    public Evento getEvento(Integer idEvento) {
        logger.info("Método getEvento. Houve Sucesso, id do evento: " + idEvento);
        return this.eventos.get(idEvento);
    }

    /**
     *
     * @param usuarioAdicionador - string
     * @param idEvento - string
     * @param participante - string
     * @throws UsuarioNaoDonoException - usuário não é dono do evento
     * @throws UsuarioNaoAmigoException - não é amigo
     * @throws LoginInvalidoException - login inexistente
     * @throws UsuarioNaoCadastradoException - usuário não cadastrado
     */
    public void adicionarParticipante(String usuarioAdicionador, Integer idEvento, String participante) throws UsuarioNaoDonoException, UsuarioNaoAmigoException, LoginInvalidoException, UsuarioNaoCadastradoException {
        if (!usuarioAdicionador.equals(getDonoEvento(idEvento))) {
            logger.info("Método adicionarParticipante. Houve Exception de usuário não é dono do evento, id do evento: " + idEvento + " Login do usuário adicionador: " + usuarioAdicionador + " Login do usuário participante: " + participante);
            throw new UsuarioNaoDonoException();
        }
        if (!this.ehAmigo(usuarioAdicionador, participante)) {
            logger.info("Método adicionarParticipante. Houve Exception de usuário não é amigo do outro, id do evento: " + idEvento + " Login do usuário adicionador: " + usuarioAdicionador + " Login do usuário participante: " + participante);
            throw new UsuarioNaoAmigoException();
        }
        logger.info("Método adicionarParticipante. Houve Sucesso, id do evento: " + idEvento + " Login do usuário adicionador: " + usuarioAdicionador + " Login do usuário participante: " + participante);
        this.eventos.get(idEvento).getListaParticipantes().add(this.getUsuario(participante));
        this.usuarios.get(participante).getListaEventosParticipa().add(this.eventos.get(idEvento));
    }

    /**
     *
     * @param idEvento
     * @return list<usuario> - participantes do evento
     */
    public String getParticipantesEvento(Integer idEvento) {
        logger.info("Método getParticipantesEvento. Houve Sucesso, id do evento: " + idEvento + " e Retorno: " + this.eventos.get(idEvento).getParticipantes());
        return this.eventos.get(idEvento).getParticipantes();
    }

    /**
     *
     * @param login
     * @return list eventos
     */
    public String getEventosUsuarioEhDono(String login) {
        logger.info("Método getEventosUsuarioEhDono. Houve Sucesso, Login do usuário: " + login + " e Retorno: " + this.usuarios.get(login).getEventos());
        return this.usuarios.get(login).getEventos();
    }

    /**
     *
     * @param login
     * @param idEvento
     * @return boolean - checar se usuário é dono do evento
     */
    public boolean ehDono(String login, Integer idEvento) {
        if (this.usuarios.get(login).getListaEventos().contains(this.eventos.get(idEvento))) {
            logger.info("Método ehDono. TRUE, Login do usuário: " + login + " e id do evento" + idEvento);
            return true;
        }
        logger.info("Método ehDono. FALSE, Login do usuário: " + login + " e id do evento" + idEvento);
        return false;
    }

    /**
     * 
     * @param idEvento
     * @param dono
     * @throws UsuarioNaoCadastradoException - usuário não cadastrado
     * @throws UsuarioNaoDonoException - usuário não é dono do evento
     * @throws EventoInexistenteException - evento não existe
     */
    public void removerEvento(Integer idEvento, String dono) throws UsuarioNaoCadastradoException, UsuarioNaoDonoException, EventoInexistenteException{
        if (!this.eventos.containsKey(idEvento)){
            logger.info("Método removerEvento. Houve Exception de Evento inexistente, id do evento: " + idEvento);
            throw new EventoInexistenteException();
        }
        if (!this.contemUsuario(dono)){
            logger.info("Método removerEvento. Houve Exception de Usuário não cadastrado, Login do usuário: " + dono);
            throw new UsuarioNaoCadastradoException();
        }
        if (!dono.equals(getDonoEvento(idEvento))){
            logger.info("Método removerEvento. Houve Exception de Usuário não é dono do evento, Login do usuário: " + dono);
             throw new UsuarioNaoDonoException();
        }
        logger.info("Método removerEvento. Houve Sucesso, Login do usuário: " + dono + " e id do evento: " + idEvento);
        for (Usuario usuario : this.eventos.get(idEvento).getListaParticipantes()){
            this.usuarios.get(usuario.getLogin()).getListaEventosParticipa().remove(this.eventos.get(idEvento));
        }
        this.usuarios.get(dono).getListaEventos().remove(this.eventos.get(idEvento));
        this.eventos.remove(idEvento);
    }

    /**
     *
     * @param login
     * @return list eventos que o usuário participa
     */
    public String getEventosUsuarioParticipa(String login){
        logger.info("Método getEventosUsuarioParticipa. Houve Sucesso, Login do usuário: " + login + " e Retorno: " + this.usuarios.get(login).getEventosParticipa());
        return this.usuarios.get(login).getEventosParticipa();
    }

    /**
     * Método que persiste as informações e limpa os usuarios da agenda
     */
    public void encerraSistema() throws IOException {
        logger.info("Método encerraSistema. Encerrando Sistema...");
        persistencia.salvar(usuarios, "arquivoUsuarios.xml");
        this.usuarios.clear();
        this.eventos.clear();
    }

    /**
     * Método que inicia o sistema persistindo as informações
     */
    public void iniciaSistema() throws ClassNotFoundException, IOException {
        logger.info("Método encerraSistema. Iniciando Sistema...");
        usuarios = persistencia.carregar("arquivoUsuarios.xml");
    }
}
